import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import { ITree, traverseTree } from '../../utils/utility';
import { Col } from '../col';
import { Icon } from '../icon';
import { Input } from '../input';
import { Row } from '../row';
import { Text } from '../text';
import { VirtualizedTree } from '../tree/VirtualizedTree';

const TRANSFER_CONTENT_HEIGHT = 330;
const TITLE_WRAPPER_HEIGHT = 40;
export interface ITransferProps {
  defaultTreeData?: ITree[];
  treeData: ITree[];
  /**
   * 是否显示过滤框
   */
  filterable?: boolean;
  filterPlaceholder?: string;
  /**
   * 选中或展开状态改变
   */
  onChange?: (treeData: ITree[]) => void;
  /**
   * 自定义过滤规则
   */
  onFilter?: (treeData: ITree[], text: string) => ITree[];
  treeBoxHeader?: React.ReactNode;
  resultBoxHeader?: React.ReactNode;
  resultBoxTitle?: React.ReactNode;
  style?: React.CSSProperties;
  className?: string;
}

export interface ITransferState {
  filterText?: string;
  filteredTreeData?: ITree[];
}

export class Transfer extends React.Component<ITransferProps, ITransferState> {
  public static porpTypes = {
    /**
     * 是否显示过滤框
     */
    filterable: PropTypes.bool,
    filterPlaceholder: PropTypes.string,
    treeBoxHeader: PropTypes.node,
    resultBoxHeader: PropTypes.node,
    resultBoxTitle: PropTypes.string,
    style: PropTypes.object,
    className: PropTypes.string,
  };
  // 如果传入了外部props，更新到state
  public static getDerivedStateFromProps(nextProps: ITransferProps, prevState: ITransferState) {
    if (prevState.filterText) {
      let filteredTreeData = Transfer.filterTreeData(nextProps.treeData, prevState.filterText);
      filteredTreeData = Transfer.copyExpandedState(prevState.filteredTreeData || [], filteredTreeData);
      return {
        ...prevState,
        filteredTreeData,
      };
    }
    return null;
  }

  public static copyExpandedState(fromTreeData: ITree[], toTreeData: ITree[]): ITree[] {
    const expandState: { [key: string]: boolean | undefined } = {};
    fromTreeData.forEach((treeNode) => {
      traverseTree(treeNode, (node) => {
        if (!Transfer.isLeaf(node)) {
          expandState[node.key] = node.expanded;
        }
      });
    });
    return Transfer.treeDataMap(toTreeData, (node) => {
      node.expanded = expandState[node.key];
      return node;
    });
  }

  public static treeDataMap(treeData: ITree[], func: (node: ITree) => ITree): ITree[] {
    return treeData.map((node) => {
      if (node.children && node.children.length) {
        return func({
          ...node,
          children: Transfer.treeDataMap(node.children, func),
        });
      }
      return func(node);
    });
  }

  public static isChecked(treeNode: ITree) {
    if (Transfer.isLeaf(treeNode)) {
      return treeNode.checked;
    }
    return !(treeNode.children || []).find((child) => !child.checked);
  }

  public static isLeaf(treeNode: ITree) {
    return !treeNode.children || !treeNode.children.length;
  }

  // filter的时候数据复制一份
  public static filterTreeData(treeData: ITree[], text: string): ITree[] {
    const filterTreeData = treeData.map((node) => {
      if (node.title.includes(text)) {
        return { ...node };
      }
      if (node.children) {
        const matchedChildren = Transfer.filterTreeData(node.children, text);
        if (matchedChildren.length) {
          return {
            ...node,
            checked: !matchedChildren.find((childNode) => !childNode.checked),
            expanded: true,
            children: matchedChildren,
          };
        }
      }
      return null;
    });

    return filterTreeData.filter((node) => node !== null) as ITree[];
  }

  public tree = React.createRef<VirtualizedTree>();

  constructor(props: ITransferProps) {
    super(props);
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleTreeChange = this.handleTreeChange.bind(this);
    this.renderResultBox = this.renderResultBox.bind(this);
    this.setNodeUnchecked = this.setNodeUnchecked.bind(this);
    this.state = {
      filterText: '',
    };
  }

  public handleInputChange(event: React.ChangeEvent<HTMLInputElement>) {
    const text = event.target.value;
    if (!text || !text.trim()) {
      this.setState({
        filteredTreeData: undefined,
        filterText: '',
      });
      // 清空文字时，复制展开状态到原始数据中
      if (this.state.filteredTreeData && this.props.onChange) {
        this.props.onChange(Transfer.copyExpandedState(this.state.filteredTreeData, this.props.treeData));
      }
    } else {
      this.setState({ filterText: text, filteredTreeData: Transfer.filterTreeData(this.props.treeData, text) });
    }
  }

  public handleTreeChange(treeData: ITree[]) {
    let newTreeData = treeData;
    if (this.state.filteredTreeData) {
      newTreeData = this.copyCheckedState(treeData, this.props.treeData);
      this.setState({ filteredTreeData: treeData });
    }
    if (this.props.onChange) {
      this.props.onChange(newTreeData);
    }
  }

  public setNodeUnchecked(treeNode: ITree) {
    const newTreeData = this.updateCheckState(this.props.treeData, treeNode.key, false);
    let newFilteredData;
    if (this.state.filteredTreeData) {
      newFilteredData = this.updateCheckState(this.state.filteredTreeData, treeNode.key, false);
      this.setState({ filteredTreeData: newFilteredData });
    }
    if (this.props.onChange) {
      this.props.onChange(newTreeData);
    }
  }

  public updateCheckState(treeData: ITree[], key: string, checked: boolean) {
    return Transfer.treeDataMap(treeData, (node) => {
      if (node.key === key) {
        return {
          ...node,
          checked,
        };
      }
      if (!Transfer.isLeaf(node)) {
        return {
          ...node,
          checked: Transfer.isChecked(node),
        };
      }
      return node;
    });
  }

  public copyCheckedState(fromTreeData: ITree[], toTreeData: ITree[]) {
    const leafs: { [key: string]: ITree } = {};
    fromTreeData.forEach((tree) => {
      traverseTree(tree, (node) => {
        if (!node.children || !node.children.length) {
          leafs[node.key] = node;
        }
      });
    });
    return Transfer.treeDataMap(toTreeData, (node) => {
      const leaf = leafs[node.key];
      if (leaf) {
        return {
          ...node,
          checked: leaf.checked,
        };
      }

      if (node.children && node.children.length) {
        return {
          ...node,
          checked: !node.children.find((child) => !child.checked),
        };
      }
      return node;
    });
  }

  // TODO: 样式部分需要抽离
  public renderResultBox() {
    const checkedNodes = VirtualizedTree.getCheckedLeafs(this.props.treeData);
    const list = checkedNodes.map((node) => {
      return (
        <li
          style={{ position: 'relative', height: '28px', lineHeight: '28px' }}
          key={'result-' + node.key}
          className="clearfix"
        >
          <Text ellipsis>{node.title}</Text>
          <Icon
            style={{
              position: 'absolute',
              right: 0,
              top: 0,
              lineHeight: '20px',
              cursor: 'pointer',
            }}
            className="pull-right"
            type="close-solid"
            color="#c21b20"
            onClick={() => this.setNodeUnchecked(node)}
          />
        </li>
      );
    });
    return <ul style={{
      backgroundColor: '#fdfdfd',
      padding: '5px 8px',
      height: this.props.resultBoxTitle ? '290px' : '330px',
      overflow: 'auto',
    }}>{list}</ul>;
  }

  public render() {
    const treeHeight = this.props.filterable ? TRANSFER_CONTENT_HEIGHT - 40 : TRANSFER_CONTENT_HEIGHT;
    return (
      <div className={classNames('br-transfer', this.props.className)}>
        <Row className="no-margin">
          <Col xs={5} className="no-padding">{this.props.treeBoxHeader}</Col>
          <Col xs={2} className="no-padding"></Col>
          <Col xs={5} className="no-padding">{this.props.resultBoxHeader}</Col>
        </Row>
        <Row className="br-transfer__content no-margin">
          <Col xs={5} className="no-padding" style={{ border: '1px solid #d4d4d4' }}>
            {
              this.props.filterable ? (
                <div className="br-transfer__input-wrapper">
                  <Input onChange={this.handleInputChange} icon={<Icon type="search" />} />
                </div>
              ) : null
            }
            <div style={{ height: treeHeight }}>
              <VirtualizedTree
                onChange={this.handleTreeChange}
                treeData={this.state.filteredTreeData || this.props.treeData}
              />
            </div>
          </Col>
          <Col xs={2} className="br-transfer__arrow-col no-padding">
            <Icon type="transfer" color="#a4a4a4" size={20} />
          </Col>
          <Col xs={5} className="no-padding" style={{ border: '1px solid #d4d4d4' }}>
            {
              this.props.resultBoxTitle ?
                <div className="br-transfer__result-title">{this.props.resultBoxTitle}</div> : null
            }
            {this.renderResultBox()}
          </Col>
        </Row>
      </div>
    );
  }
}
